﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MapGenerator : MonoBehaviour {
	public GameObject TilesContainer;	//padre de los tiles para ordenar un poco.
	public GameObject Floor;			//tile Walkable
	public GameObject Wall;				//tile not Walkable
    public GameObject Escalera;         //TileNextLevel.
	public GameObject Enemy;			//prefab enemigo
	public Vector3 PlayerStartPosition;	//donde comienza el player
	public map theMap;					//el mapa
	private List<map> RoomList = new List<map> ();	//Lista de rooms para unir
	private List<GameObject> EnemyList = new List<GameObject> ();	//Lista de enemigos
	public enum Tiles
		{
			floor=0,
			wall,
            Escalera
		}
	public void MakeMap(int width, int height, int minRooms, int MaxRooms, int minRoomWidth, int maxRoomWidth,int minRoomHeight, int maxRoomHeight){
		//lleno todo el mapa de paredes
		theMap = new map (width, height);
		for(int i = 0; i<theMap.matrix.GetLength (0); i++){//x
			for (int z = 0; z < theMap.matrix.GetLength(1); z++) {//y
				theMap.matrix[i,z]= (int)Tiles.wall;
			}
		}
		//*********************************************************
		
		//creo un random entre minrooms y maxrooms para saber cuantos rooms crear.
		int rooms = Random.Range (minRooms, MaxRooms);
		
		//Mientras no haya la cantidad de rooms, sigo iterando hasta que se cree un room.
		//Advertencia de posible Bug o problema de eficiencia.
		while (RoomList.Count!=rooms) {
			map room=MakeRoom(minRoomWidth,maxRoomWidth,minRoomHeight,maxRoomHeight);
			InsertRoom (room);
		}
		//**********BOOM*******************************************
		
		//recorro la lista de rooms.
		//partiendo del primer room, creo un pasillo al segundo, del segundo al tercero, etc.
		for(int i = 0; i<RoomList.Count-1; i++){//x
			//creo el tunel horizontal.
			createHTunnel(RoomList[i].WorldCenterX,RoomList[i+1].WorldCenterX,RoomList[i].WorldCenterY);
			//creo el tunel vertical.
			createVTunnel(RoomList[i].WorldCenterY,RoomList[i+1].WorldCenterY,RoomList[i+1].WorldCenterX);
		}
        //meto la escalera de salida.
        theMap.matrix[RoomList[RoomList.Count-1].WorldCenterX, RoomList[RoomList.Count-1].WorldCenterY]=(int)Tiles.Escalera;
		//Limpio el mapa de Tiles innecesarios(Falta optimizar).
		LoadMap (0);
		CleanMap ();
		for (int i = 0; i<RoomList.Count-1; i++) {
			EnemyList.Add(Instantiate (Enemy,new Vector3(RoomList[i].OffsetX,0,RoomList[i].OffsetY),Quaternion.identity) as GameObject);
		}
		//Setea la posicion inicial donde ira el personaje.
		PlayerStartPosition = new Vector3 (RoomList[0].WorldCenterX,0,RoomList[0].WorldCenterY);
		//Debug.Log(PlayerStartPosition.ToString());
	}
	// Update is called once per frame
	void Update () {
	
	}
	public void ReMakeMap(int width, int height, int minRooms, int MaxRooms, int minRoomWidth, int maxRoomWidth,int minRoomHeight, int maxRoomHeight){
		DeleteMap ();
		MakeMap (width, height,minRooms,MaxRooms, minRoomWidth,maxRoomWidth,minRoomHeight,maxRoomHeight);
	}
	public Vector3 GetStartPosition(){
		return PlayerStartPosition;
	}
	private map MakeRoom(int minRoomWidth,int maxRoomWidth, int minRoomHeigth, int maxRoomHeigth){
		int w=Random.Range(minRoomWidth,maxRoomWidth);
		int h=Random.Range(minRoomHeigth,maxRoomHeigth);
		map a = new map (w, h);
		for(int i = 0; i<a.matrix.GetLength (0); i++){//x
			for (int z = 0; z < a.matrix.GetLength(1); z++) {//y
				a.matrix[i,z]= (int)Tiles.floor;
			}
		}
		return a;
	}
	private void InsertRoom(map room){
		do {
			//genera un offset random en el array desde [1,1] hasta [tam-1,tam-1)
			room.OffsetX = Random.Range (1, theMap.matrix.GetLength (0) - (room.matrix.GetLength (0) + 2)); //check
			room.OffsetY = Random.Range (1, theMap.matrix.GetLength (1) - (room.matrix.GetLength (1) + 2)); //check
		} while(ComprobateOffset(room)!=true);
		for(int i = room.OffsetX; i<room.matrix.GetLength(0)+room.OffsetX; i++){//x
			for (int z = room.OffsetY; z < room.matrix.GetLength(1)+room.OffsetY; z++) {//y
				theMap.matrix[i,z] = (int)Tiles.floor; //a chusmear
			}
		}
		room.CalcPos ();
		RoomList.Add (room);
	}
	private bool ComprobateOffset(map a){
		int count=0;
		int isTrue = (a.matrix.GetLength (0)+1) * (a.matrix.GetLength (1)+1);
		for(int i = a.OffsetX; i < a.matrix.GetLength (0)+a.OffsetX+1; i++){//x
			for (int z = a.OffsetY; z < a.matrix.GetLength(1)+a.OffsetY+1; z++) {//y
				if(theMap.matrix[i,z] != (int)Tiles.floor)
					count ++;
			}
		}
		if (count == isTrue) {
			return true;
		}
		return false;
	}
	private void LoadMap(int y){
		for(int i = 0; i<theMap.matrix.GetLength (0); i++){//x
			for (int z = 0; z < theMap.matrix.GetLength(1); z++) {//y
				if(theMap.matrix[i,z] != -1){
					switch (theMap.matrix[i,z]) {
					case (int)Tiles.floor: 
						//Debug.Log("piso");
						theMap.Tiles[i,z]=(Instantiate(Floor,new Vector3(i,y,z),Quaternion.identity) as GameObject);
						theMap.Tiles[i,z].transform.parent = TilesContainer.transform;
						break;
					case (int)Tiles.wall: 
						//Debug.Log("pared");
						theMap.Tiles[i,z]=(Instantiate(Wall,new Vector3(i,y,z),Quaternion.identity) as GameObject);
						theMap.Tiles[i,z].transform.parent = TilesContainer.transform;
						break;
                    case (int)Tiles.Escalera:
                        //Debug.Log("pared");
                        theMap.Tiles[i, z] = (Instantiate(Escalera, new Vector3(i, y, z), Quaternion.identity) as GameObject);
                        theMap.Tiles[i, z].transform.parent = TilesContainer.transform;
                        break;
					default:
						//Debug.Log("que mierda pusiste boludo");
					break;
					}
				}
			}
		}
	}
	
	//Limpia los tiles innecesarios del mapa.
	private void CleanMap(){
		List<GameObject> TilesToDelete = new List<GameObject> (); //Almacena los tiles a ser borrados.
		for(int i = 0; i < theMap.matrix.GetLength (0); i++){//x
			for (int z = 0; z < theMap.matrix.GetLength(1); z++) {//y
				if(theMap.matrix[i,z].Equals((int)Tiles.wall)){
					int minX = i-1<0? 0:i-1;
					int maxX = i+1>theMap.matrix.GetLength(0)? theMap.matrix.GetLength(0):i+1;
					int minY = z-1<0? 0:z-1;
					int maxY = z+1>theMap.matrix.GetLength(1)? theMap.matrix.GetLength(1):z+1;
					/*recorre el mini array a comprobar
					tomando como 0,0 posicion-1 y llegando a posicion+1(2,2).*/
					bool delete = true;
					for(int j = minX; j<maxX;j++){
						for(int k = minY;k<maxY;k++){
							if(theMap.matrix[j,k].Equals((int)Tiles.floor)){
								delete = false;
							}
						}
					}
					if(delete){
						theMap.matrix[i,z] = -1;
					}
				}
			}
		}
	}
	
	//Borra todo el mapa de escena y de memoria.
	private void DeleteMap(){
		//Limpia la escena de tiles.
		for(int i = 0; i < theMap.matrix.GetLength (0); i++){//x
			for (int z = 0; z < theMap.matrix.GetLength(1); z++) {//y
				Destroy(theMap.Tiles[i,z]);
			}
		}
		//setea el mapa de tiles a null(borra todo el contenido).
		theMap = null;
		//borra la lista de rooms.
		RoomList.Clear ();
		foreach (var item in EnemyList) {
			Destroy(item);
		}
		EnemyList.Clear ();
	}
	
	/*crea un tunel horizontal, con el valo de x de cada cuarto, toma el minimo y rellena de piso hasta el maximo.
	y es para saber donde se posiciona el principio vector2(x,y);
	createVTunnel(primerCuartoX, segundoCuartoX, primerCuartoY);*/
	private void createHTunnel(int x1, int x2, int y)
	{
		for (int x = Mathf.Min(x1, x2); x < Mathf.Max(x1, x2) + 1; x++)
		{
			theMap.matrix[x,y] = (int)Tiles.floor;
		}
	}
	
	/*crea un tunel vertical, con el valo de y de cada cuarto, toma el minimo y rellena de piso hasta el maximo.
	x es para saber donde se posiciona el principio vector2(x,y);
	createVTunnel(primerCuartoY, segundoCuartoY, primerCuartoX);*/
	private void createVTunnel(int y1, int y2, int x)
	{
		for (int y = Mathf.Min( y1, y2 ); y < Mathf.Max(y1, y2) + 1; y++)
		{
			theMap.matrix[x,y] = (int)Tiles.floor;
		}
	}
}
public class map{
	public int[,] matrix;
	public GameObject[,] Tiles;
	public int OffsetX, OffsetY;
	public int CenterX, CenterY;
	public int WorldCenterX, WorldCenterY;
	//calcula la posicion en mundo del cuarto.
	public void CalcPos(){
		WorldCenterX = OffsetX + CenterX;
		WorldCenterY = OffsetY + CenterY;
	}
	public map(int w, int h){
		CenterX = (int)(w / 2);
		CenterY = (int)(h / 2);
		matrix = new int[w,h];
		Tiles = new GameObject[w, h];
	}
}
